/*
 * Galaxium Messenger
 * 
 * Copyright (C) 2007 Philippe Durand <draekz@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Collections.Generic;

using Gtk;
using Glade;

using Galaxium.Core;
using Galaxium.Gui;
using Galaxium.Gui.GtkGui;

using Anculus.Core;

namespace Galaxium.Protocol.Irc.GtkGui
{
	public sealed class ManageNetworksDialog
	{
		[Widget ("ManageNetworksDialog")]
		private Dialog _dialog;
		[Widget ("imgDialog")]
		private Image _dialogImage;
		[Widget ("treeNetworks")]
		private TreeView _treeNetworks;
		[Widget ("treeMirrors")]
		private TreeView _treeMirrors;
	//	[Widget ("expanderDetails")]
	//	private Expander _expanderDetails;
		[Widget ("buttonRemoveNetwork")]
		private Button _buttonRemoveNetwork;
		[Widget ("buttonRemoveMirror")]
		private Button _buttonRemoveMirror;
		[Widget ("buttonAddNetwork")]
		private Button _buttonAddNetwork;
		[Widget ("buttonAddMirror")]
		private Button _buttonAddMirror;
		
		private IConfigurationSection _config = Configuration.Protocol.Section["IRC"];
		private ListStore _networkStore;
		private ListStore _mirrorStore;
		private Dictionary<IrcNetworkInfo, TreeIter> _networkLookup = new Dictionary<IrcNetworkInfo, TreeIter> ();
		private Dictionary<IrcServerInfo, TreeIter> _mirrorLookup = new Dictionary<IrcServerInfo, TreeIter> ();
		private IList<IrcNetworkInfo> _networks;
		
		public ManageNetworksDialog (string selectedNetwork)
		{
			XML gxml = new XML (GladeUtility.GetGladeResourceStream (typeof (ManageNetworksDialog).Assembly, "ManageNetworksDialog.glade"), null, null);
			gxml.Autoconnect (this);
			
			_buttonAddNetwork.Clicked += ButtonAddNetworkClicked;
			_buttonAddMirror.Clicked += ButtonAddMirrorClicked;
			_buttonRemoveNetwork.Clicked += ButtonRemoveNetworkClicked;
			_buttonRemoveMirror.Clicked += ButtonRemoveMirrorClicked;
			
			GtkUtility.EnableComposite (_dialog);
			
			_dialog.Icon = IconUtility.GetIcon ("galaxium-server", IconSizes.Small);
			_dialogImage.Pixbuf = IconUtility.GetIcon ("galaxium-server", IconSizes.Large);
			
			_networkStore = new ListStore(typeof(IrcNetworkInfo));
			_mirrorStore = new ListStore(typeof(IrcServerInfo));
			
			_treeNetworks.Model = _networkStore;
			_treeNetworks.HeadersClickable = true;
			_treeNetworks.Selection.Changed += NetworkSelectionChanged;
			
			_treeMirrors.Model = _mirrorStore;
			_treeMirrors.HeadersClickable = true;
			_treeMirrors.Selection.Changed += MirrorSelectionChanged;
			
			// Columns for Network Tree
			TreeViewColumn networkColumn = new TreeViewColumn ();
			networkColumn.Title = "Network Name";
			CellRendererText nameCell = new CellRendererText ();
			networkColumn.PackStart (nameCell, true);
 			networkColumn.Expand = true;
 			networkColumn.Sizing = TreeViewColumnSizing.Autosize;
			
			TreeViewColumn mirrorsColumn = new TreeViewColumn ();
			mirrorsColumn.Title = "Mirrors";
			CellRendererText mirrorsCell = new CellRendererText ();
			mirrorsColumn.PackStart (mirrorsCell, true);
			mirrorsColumn.Expand = false;
			mirrorsColumn.Sizing = TreeViewColumnSizing.Autosize;
			//mirrorsColumn.MinWidth = 60;
			mirrorsColumn.Alignment = 1.0f;
			
 			networkColumn.SetCellDataFunc (nameCell, new TreeCellDataFunc (RenderName));
			mirrorsColumn.SetCellDataFunc (mirrorsCell, new TreeCellDataFunc (RenderMirrors));
			
			_treeNetworks.AppendColumn (networkColumn);
			_treeNetworks.AppendColumn (mirrorsColumn);
			
			// Columns for Mirror Tree
			TreeViewColumn serverColumn = new TreeViewColumn ();
			serverColumn.Title = "Hostname";
			CellRendererText serverCell = new CellRendererText ();
			serverColumn.PackStart (serverCell, true);
			serverColumn.Expand = true;
			serverColumn.Sizing = TreeViewColumnSizing.Autosize;
			
			TreeViewColumn portColumn = new TreeViewColumn ();
			portColumn.Title = "Port";
			CellRendererText portCell = new CellRendererText ();
			portColumn.PackStart (portCell, true);
			portColumn.Expand = false;
			portColumn.Sizing = TreeViewColumnSizing.Autosize;
			//portColumn.MinWidth = 75;
			portColumn.Alignment = 1.0f;
			
			serverColumn.SetCellDataFunc (serverCell, new TreeCellDataFunc (RenderServer));
			portColumn.SetCellDataFunc (portCell, new TreeCellDataFunc (RenderPort));
			
			_treeMirrors.AppendColumn (serverColumn);
			_treeMirrors.AppendColumn (portColumn);
			
			LoadNetworkInfo ();
			
			SelectNetwork (selectedNetwork);
			
			_dialog.ShowAll();
		}
		
		public int Run()
		{
			return _dialog.Run();
		}
		
		public void Destroy()
		{
			if (_networks != null)
				SaveNetworkInfo ();
			
			_dialog.Hide();
			_dialog.Destroy();
		}
		
		private void NetworkSelectionChanged (object sender, EventArgs args)
		{
			UpdateSelectedNetwork ();
		}
		
		private void MirrorSelectionChanged (object sender, EventArgs args)
		{
			if (_treeMirrors.Selection != null)
			{
				TreeIter iter = TreeIter.Zero;
				
				_treeMirrors.Selection.GetSelected (out iter);
				
				if (iter.Stamp == TreeIter.Zero.Stamp)
					return;
				
				IrcServerInfo mirror = (IrcServerInfo)_mirrorStore.GetValue(iter, 0);
				
				if (mirror != null)
				{
					_buttonRemoveMirror.Sensitive = true;
				}
			}
			else
			{
				_buttonRemoveMirror.Sensitive = false;
			}
		}
		
		private void ButtonAddNetworkClicked (object sender, EventArgs args)
		{
			AddNetworkDialog dialog = new AddNetworkDialog ();
			
			if ((ResponseType)dialog.Run() == ResponseType.Ok)
			{
				IrcNetworkInfo network = NewNetwork (dialog.Name, dialog.Host, Int32.Parse(dialog.Port));
				
				if (network != null)
				{
					_networks.Add (network);
					
					AddNetworkToTree (network);
					
					SelectNetwork (network.Name);
					
					SaveNetworkInfo ();
				}
			}
			
			dialog.Destroy ();
		}
		
		private void ButtonRemoveNetworkClicked (object sender, EventArgs args)
		{
			IrcNetworkInfo network = GetSelectedNetwork ();
			
			if (network != null)
			{
				_networks.Remove (network);
				
				RemoveNetworkFromTree (network);
				
				SaveNetworkInfo ();
			}
		}
		
		private void ButtonAddMirrorClicked (object sender, EventArgs args)
		{
			AddMirrorDialog dialog = new AddMirrorDialog ();
			
			if ((ResponseType)dialog.Run() == ResponseType.Ok)
			{
				IrcNetworkInfo network = GetSelectedNetwork ();
				
				if (network != null)
				{
					IrcServerInfo mirror = NewMirror (network, dialog.Host, Int32.Parse (dialog.Port));
					
					AddMirrorToTree (mirror);
					
					_treeNetworks.QueueDraw();
					
					SelectMirror (mirror.Hostname);
					
					SaveNetworkInfo ();
				}
			}
			
			dialog.Destroy ();
		}
		
		private void ButtonRemoveMirrorClicked (object sender, EventArgs args)
		{
			IrcNetworkInfo network = GetSelectedNetwork ();
			
			if (network != null)
			{
				IrcServerInfo mirror = GetSelectedMirror ();
				
				if (mirror != null)
				{
					RemoveMirror (network, mirror);
					
					RemoveMirrorFromTree (mirror);
					
					_treeNetworks.QueueDraw();
					
					SaveNetworkInfo ();
				}
			}
		}
		
		private void RenderName (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			IrcNetworkInfo network = (IrcNetworkInfo) model.GetValue (iter, 0);
			
			(cell as CellRendererText).Text = network.Name;
			(cell as CellRendererText).Ellipsize = Pango.EllipsizeMode.End;
			(cell as CellRendererText).Editable = true;
			(cell as CellRendererText).Edited += NameEdited;
		}
		
		private void RenderMirrors (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			IrcNetworkInfo network = (IrcNetworkInfo) model.GetValue (iter, 0);
			
			(cell as CellRendererText).Text = network.Mirrors.Length.ToString();
			(cell as CellRendererText).Ellipsize = Pango.EllipsizeMode.End;
			
			cell.Xalign = 1.0f;
		}
		
		private void RenderServer (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			IrcServerInfo server = (IrcServerInfo) model.GetValue (iter, 0);
			
			(cell as CellRendererText).Text = server.Hostname;
			(cell as CellRendererText).Ellipsize = Pango.EllipsizeMode.End;
			(cell as CellRendererText).Editable = true;
			(cell as CellRendererText).Edited += ServerEdited;
		}
		
		private void RenderPort (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			IrcServerInfo server = (IrcServerInfo) model.GetValue (iter, 0);
			
			(cell as CellRendererText).Text = server.Port.ToString();
			(cell as CellRendererText).Ellipsize = Pango.EllipsizeMode.End;
			(cell as CellRendererText).Editable = true;
			(cell as CellRendererText).Edited += PortEdited;
			
			cell.Xalign = 1.0f;
		}
		
		void NameEdited (object sender, EditedArgs args)
		{
			TreeIter iter;
			_networkStore.GetIter (out iter, new TreePath (args.Path));
			
			IrcNetworkInfo network = (IrcNetworkInfo)_networkStore.GetValue (iter, 0);
			
			network.Name = args.NewText;
			
			_treeNetworks.ColumnsAutosize ();
		}
		
		void ServerEdited (object sender, EditedArgs args)
		{
			TreeIter iter;
			_mirrorStore.GetIter (out iter, new TreePath(args.Path));
			
			IrcServerInfo server = (IrcServerInfo)_mirrorStore.GetValue (iter, 0);
			
			server.Hostname = args.NewText;
			
			_treeMirrors.ColumnsAutosize();
		}
		
		void PortEdited (object sender, EditedArgs args)
		{
			TreeIter iter;
			_mirrorStore.GetIter(out iter, new TreePath(args.Path));
			
			IrcServerInfo server = (IrcServerInfo)_mirrorStore.GetValue (iter, 0);
			
			try
			{
				server.Port = Int32.Parse (args.NewText);
			}
			catch
			{
				
			}
			
			_treeMirrors.ColumnsAutosize ();
		}
		
		private IrcServerInfo GetSelectedMirror ()
		{
			if (_treeMirrors.Selection != null)
			{
				TreeIter iter = TreeIter.Zero;
				
				_treeMirrors.Selection.GetSelected (out iter);
				
				if (iter.Stamp == TreeIter.Zero.Stamp)
					return null;
				
				IrcServerInfo mirror = (IrcServerInfo)_mirrorStore.GetValue(iter, 0);
				
				if (mirror != null)
					return mirror;
			}
			
			return null;
		}
		
		private IrcNetworkInfo GetSelectedNetwork ()
		{
			if (_treeNetworks.Selection != null)
			{
				TreeIter iter = TreeIter.Zero;
				
				_treeNetworks.Selection.GetSelected (out iter);
				
				if (iter.Stamp == TreeIter.Zero.Stamp)
					return null;
				
				IrcNetworkInfo network = (IrcNetworkInfo)_networkStore.GetValue(iter, 0);
				
				if (network != null)
					return network;
			}
			
			return null;
		}
		
		private void UpdateSelectedNetwork ()
		{
			if (_treeNetworks.Selection != null)
			{
				TreeIter iter = TreeIter.Zero;
				
				_treeNetworks.Selection.GetSelected (out iter);
				
				if (iter.Stamp == TreeIter.Zero.Stamp)
					return;
				
				IrcNetworkInfo network = (IrcNetworkInfo)_networkStore.GetValue(iter, 0);
				
				if (network != null)
				{
					_buttonRemoveNetwork.Sensitive = true;
					_buttonAddMirror.Sensitive = true;
					_buttonRemoveMirror.Sensitive = true;
					
					ClearMirrorTree ();
					
					foreach (IrcServerInfo server in network.Mirrors)
						AddMirrorToTree (server);
					
					SelectFirstMirror ();
				}
			}
			else
			{
				_buttonAddMirror.Sensitive = false;
				_buttonRemoveMirror.Sensitive = false;
				_buttonRemoveNetwork.Sensitive = false;
			}
		}
		
		private IrcNetworkInfo NewNetwork (string name, string host, int port)
		{
			IrcNetworkInfo network = new Galaxium.Protocol.Irc.IrcNetworkInfo (name);
			
			network.Mirrors = new Galaxium.Protocol.Irc.IrcServerInfo [] { new IrcServerInfo(host, port) };
			
			return (network);
		}
		
		private IrcServerInfo NewMirror (IrcNetworkInfo network, string host, int port)
		{
			List<IrcServerInfo> mirrors = new List<IrcServerInfo> ();
			IrcServerInfo newMirror;
			
			foreach (IrcServerInfo mirror in network.Mirrors)
				mirrors.Add (mirror);
			
			newMirror = new IrcServerInfo (host, port);
			
			mirrors.Add (newMirror);
			
			network.Mirrors = mirrors.ToArray ();
			
			return (newMirror);
		}
		
		private void RemoveMirror (IrcNetworkInfo network, IrcServerInfo mirror)
		{
			List<IrcServerInfo> mirrors = new List<IrcServerInfo> ();
			
			foreach (IrcServerInfo cycle in network.Mirrors)
				if (cycle != mirror)
					mirrors.Add (cycle);
			
			network.Mirrors = mirrors.ToArray ();
		}
		
		private void LoadNetworkInfo ()
		{
			if (_config.ContainsKey ("Networks"))
				_networks = _config.GetList<IrcNetworkInfo> ("Networks");
			else
			{
				List<IrcNetworkInfo> stored = new List<IrcNetworkInfo> (IrcProtocolHelper.GetAllStoredNetworkInfo ());
				
				stored.Sort ();
				
				if (_networks != null)
					foreach (IrcNetworkInfo network in _networks)
						RemoveNetworkFromTree (network);
				
				_networks = stored;
				
				SaveNetworkInfo ();
			}
			
			foreach (IrcNetworkInfo network in _networks)
				AddNetworkToTree (network);
		}
		
		private void SaveNetworkInfo ()
		{
			_config.SetList<IrcNetworkInfo> ("Networks", _networks);
		}
		
		private void SelectNetwork (string network)
		{
			foreach (IrcNetworkInfo info in _networkLookup.Keys)
			{
				if (info.Name == network)
				{
					TreePath path = _networkStore.GetPath(_networkLookup[info]);
					_treeNetworks.SetCursor (path, null, false);
				}
			}
		}
		
		private void SelectMirror (string mirror)
		{
			foreach (IrcServerInfo info in _mirrorLookup.Keys)
			{
				if (info.Hostname == mirror)
				{
					TreePath path = _mirrorStore.GetPath(_mirrorLookup[info]);
					_treeMirrors.SetCursor (path, null, false);
				}
			}
		}
		
		private void SelectFirstNetwork ()
		{
			TreeIter iter;
			
			if(_networkStore.GetIterFirst (out iter))
			{
				TreePath path = _networkStore.GetPath (iter);
				_treeNetworks.SetCursor (path, null, false);
			}
		}
		
		private void SelectFirstMirror ()
		{
			TreeIter iter;
			
			if(_mirrorStore.GetIterFirst (out iter))
			{
				TreePath path = _mirrorStore.GetPath (iter);
				_treeMirrors.SetCursor (path, null, false);
			}
		}
		
		private void AddMirrorToTree (IrcServerInfo mirror)
		{
			TreeIter iter = _mirrorStore.AppendValues (mirror);
			_mirrorLookup.Add (mirror, iter);
		}
		
		private void RemoveMirrorFromTree (IrcServerInfo mirror)
		{
			bool selectedRemoved = false;
			
			TreeIter selectedIter = TreeIter.Zero;
			TreeIter iter = _mirrorLookup[mirror];
			
			_treeMirrors.Selection.GetSelected (out selectedIter);
			
			if (iter.Stamp == selectedIter.Stamp)
				selectedRemoved = true;
			
			_mirrorStore.Remove(ref iter);
			
			if (selectedRemoved)
				SelectFirstMirror ();
		}
		
		private void ClearMirrorTree ()
		{
			_mirrorStore.Clear ();
			_mirrorLookup.Clear ();
		}
		
		private void AddNetworkToTree (IrcNetworkInfo network)
		{
			TreeIter iter = _networkStore.AppendValues (network);
			_networkLookup.Add (network, iter);
		}
		
		private void RemoveNetworkFromTree (IrcNetworkInfo network)
		{
			bool selectedRemoved = false;
			
			TreeIter selectedIter = TreeIter.Zero;
			TreeIter iter = _networkLookup[network];
			
			_treeNetworks.Selection.GetSelected (out selectedIter);
			
			if (iter.Stamp == selectedIter.Stamp)
				selectedRemoved = true;
			
			_networkStore.Remove(ref iter);
			
			if (selectedRemoved)
				SelectFirstNetwork ();
		}
	}
}